package com.ybear.ybutils.utils;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.text.TextUtils;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Space;

import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import androidx.core.util.Consumer;

import com.ybear.ybutils.utils.handler.HandlerManage;

import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 系统工具类
 */
public class SysUtil {
    @IntDef({ StatusBarIconColor.WHITE, StatusBarIconColor.BLACK })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StatusBarIconColor {
        @ColorInt int WHITE = Color.WHITE;
        @ColorInt int BLACK = Color.BLACK;
    }

    public interface OnWindowVisibleDisplayListener {
        void onDisplay(int rootInvisibleHeight, boolean isDisplay);
    }
    private static ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener;
    private static final List<OnWindowVisibleDisplayListener> mOnWindowVisibleDisplayList = new ArrayList<>();
    private static final Rect mWindowVisibleDisplayRect = new Rect();
    private static int mRootInvisibleHeight = 0;

    /**
     * 获取屏幕宽度
     * @param context   上下文
     * @return          宽度
     */
    public static int getScreenWidth(Context context) {
        return context.getResources().getDisplayMetrics().widthPixels;
    }

    /**
     * 获取屏幕高度
     * @param context   上下文
     * @return          高度
     */
    public static int getScreenHeight(Context context) {
        return context.getResources().getDisplayMetrics().heightPixels;
    }

    /**
     * 获取屏幕真实宽高
     * @param context   上下文
     * @return          真实宽高
     */
    @Nullable
    public static Point getScreenTrueSize(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        if( wm == null ) return null;
        Display display = wm.getDefaultDisplay();
        Point p = new Point();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            display.getRealSize( p );
        }else {
            display.getSize( p );
        }
        return p;
    }

    /**
     * 获取屏幕真实宽度
     * @param context   上下文
     * @return          真实宽度
     */
    public static int getScreenTrueWidth(Context context) {
        Point p = getScreenTrueSize( context );
        return p == null ? getScreenWidth( context ) : p.x;
    }

    /**
     * 获取屏幕真实高度
     * @param context   上下文
     * @return          真实高度
     */
    public static int getScreenTrueHeight(Context context) {
        Point p = getScreenTrueSize( context );
        return p == null ? getScreenHeight( context ) : p.y;
    }

    /**
     * 获取当前包的信息
     * @param mContext  上下文
     * @return          {@link PackageInfo}
     */
    public static @Nullable PackageInfo getPackageInfo(@NonNull Context mContext) {
        PackageManager pm = mContext.getPackageManager();
        try {
            return pm.getPackageInfo(mContext.getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 沉浸式
     */
    public static void immersiveStatusBar(Window w) {
        int sdk = Build.VERSION.SDK_INT;
        //允许绘制状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        }
        /* 沉浸式 */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            w.clearFlags(
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
                            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
            );
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            w.getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            );
        }
        if ( sdk >= Build.VERSION_CODES.LOLLIPOP ) {
            //透明状态栏
            w.setStatusBarColor(Color.TRANSPARENT);
        }
    }

    /**
     * 设置状态栏图标颜色
     * @param w             当前窗口
     * @param color         设置的颜色
     */
    public static void setStatusBarIconColor(@NonNull Window w, @StatusBarIconColor int color) {
        boolean isBlack = color == StatusBarIconColor.BLACK;
        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ){
            View decorView = w.getDecorView();
            int vis = decorView.getSystemUiVisibility();
            if( isBlack ){
                vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            } else{
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            }
            decorView.setSystemUiVisibility( vis );
        }else if( isBlack && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
            w.setStatusBarColor( color );
        }
    }

    /**
     * 获取状态栏高度
     * @return      高度
     */
    public static int getStatusBarHeight() {
        int result = 0;
        try {
            int id = Resources.getSystem().getIdentifier(
                    "status_bar_height", "dimen", "android"
            );
            if( id > 0 ) result = Resources.getSystem().getDimensionPixelSize( id );
        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 设置View的topPadding高度为状态栏高度
     * @param v         View
     * @param height    传入{@link Integer#MIN_VALUE}时，自动填充状态栏高度
     */
    public static void setStatusBarHeightForPadding(View v, int height) {
        if( v == null ) return;
        v.setPadding(
                v.getPaddingLeft(),
                height == Integer.MIN_VALUE ? getStatusBarHeight() : height,
                v.getPaddingRight(),
                v.getPaddingBottom()
        );
    }
    /**
     * 设置View的topPadding高度为状态栏高度
     * @param v     View
     */
    public static void setStatusBarHeightForPadding(View v) {
        setStatusBarHeightForPadding( v, Integer.MIN_VALUE );
    }

    /**
     * 设置Space的高度为状态栏高度
     * @param s     Space
     * @param height    传入{@link Integer#MIN_VALUE}时，自动填充状态栏高度
     */
    public static void setStatusBarHeightForSpace(Space s, int height) {
        if( s == null ) return;
        ViewGroup.LayoutParams lp = s.getLayoutParams();
        int h = height == Integer.MIN_VALUE ? getStatusBarHeight() : height;
        if( lp == null ) {
            lp = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, h );
        }else {
            lp.height = h;
        }
        s.setLayoutParams( lp );
    }
    /**
     * 设置Space的高度为状态栏高度
     * @param s     Space
     */
    public static void setStatusBarHeightForSpace(Space s) {
        setStatusBarHeightForSpace( s, Integer.MIN_VALUE );
    }

    /**
     * 启用全屏
     * @param w     {@link Activity#getWindow()}
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static void setFullScreen(@NonNull Window w, boolean enable) {
        if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ) return;
        w.getDecorView().setSystemUiVisibility(enable ?
                View.SYSTEM_UI_FLAG_IMMERSIVE |
                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_FULLSCREEN :
                View.SYSTEM_UI_FLAG_VISIBLE
        );
    }

    /**
     * 杀死指定包名的app
     * @param context       上下文
     * @param pageName      包名
     */
    public static void killProcesses(Context context, String pageName) {
        if( context == null || TextUtils.isEmpty( pageName ) ) return;
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if( am != null ) am.killBackgroundProcesses( pageName );
    }

    @Nullable
    public static InputMethodManager getInputMethodManager(Context context) {
        if( context == null ) return null;
        return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    }


    /**
     * 显示软键盘
     * @param view  需要显示的View
     * @param <V>   该View必须继承 {@link View}
     * @return      显示已显示
     */
    public static <V extends View> boolean showKeyboard(V view) {
        if( view == null ) return false;
        InputMethodManager imm = getInputMethodManager( view.getContext() );
        if( imm == null ) return false;
        view.setFocusable( true );
        view.setFocusableInTouchMode( true );
        view.requestFocus();
        if( !imm.showSoftInput( view, 0 ) ) {
            imm.toggleSoftInput( 0, InputMethodManager.HIDE_NOT_ALWAYS );
            return true;
        }
        return false;
    }

    /**
     * 关闭软键盘
     * @param activity  上下文
     * @return          是否已关闭
     */
    public static boolean hideKeyboard(Activity activity) {
        if( activity == null ) return false;
        InputMethodManager imm = getInputMethodManager( activity );
        if( imm == null ) return false;
        View cfView = activity.getCurrentFocus();
        if( cfView == null ) return false;
        IBinder ib = cfView.getWindowToken();
        if( ib == null || !imm.isActive() ) return false;
        boolean result = imm.hideSoftInputFromWindow( ib, InputMethodManager.HIDE_NOT_ALWAYS );
        cfView.clearFocus();
        return result;
    }

    private static long mAutoHideKeyboardTime;
    /**
     * 自动隐藏键盘并且清除当前焦点
     * @param activity  活动页
     * @param ev        {@link Activity#dispatchTouchEvent(MotionEvent)}
     *                  {@link Activity#onTouchEvent(MotionEvent)}
     * @param xy        用于记录坐标，可以定义为常量
     */
    public static void autoHideKeyboardAndClearFocus(Activity activity,
                                                     @NonNull MotionEvent ev,
                                                     @NonNull int[] xy) {
        if( activity == null ) return;
        int action = ev.getAction();
        if( action == MotionEvent.ACTION_DOWN ) mAutoHideKeyboardTime = System.currentTimeMillis();
        if( action == MotionEvent.ACTION_UP ) {
            if( System.currentTimeMillis() - mAutoHideKeyboardTime > 150 ) return;
        }else {
            return;
        }

        View focusView = activity.getCurrentFocus();
        if( focusView == null ) return;
        focusView.getLocationOnScreen( xy );
        float evX = ev.getX();
        float evY = ev.getY();
        int fvX1 = xy[ 0 ];
        int fvY1 = xy[ 1 ];
        int fvX2 = fvX1 + focusView.getWidth();
        int fvY2 = fvY1 + focusView.getHeight();
        if( ( evX < fvX1 || evX > fvX2 ) || ( evY < fvY1 || evY > fvY2 ) ) {
            SysUtil.hideKeyboard( activity );
            focusView.clearFocus();
        }
    }

    /**
     * dialog自动弹出键盘
     * @param context   上下文
     * @param d         Dialog
     * @param et        Dialog下的编辑框
     * @param l         调用了{@link android.content.DialogInterface.OnShowListener}，用于返回
     */
    public static void autoPopUpKeyboard(@NonNull Context context,
                                         @NonNull EditText et,
                                         @NonNull Dialog d,
                                         DialogInterface.OnShowListener l) {
        d.setOnShowListener(dialog -> {
            InputMethodManager imm = getInputMethodManager( context );
            if( imm != null ) imm.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT);
            if( l != null ) l.onShow( dialog );
        });
    }

    /**
     * dialog自动弹出键盘
     * @param context   上下文
     * @param d         Dialog
     * @param et        Dialog下的编辑框
     */
    public static void autoPopUpKeyboard(@NonNull Context context,
                                         @NonNull EditText et,
                                         @NonNull Dialog d) {
        autoPopUpKeyboard( context, et, d, null );
    }

    /**
     * 获取语言环境
     * @return  语言环境
     */
    public static Locale getLocale() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            return LocaleList.getDefault().get( 0 );
        }else {
            return Locale.getDefault();
        }
    }

    /**
     * 获取本机语言
     * @param isCountry     是否返回语言的地区，eg：CN, TW(China)
     * @return  语言。eg：zh-CN
     */
    public static String getLanguage(boolean isCountry) {
        Locale locale = getLocale();
        return isCountry ?
                String.format( "%s-%s", locale.getLanguage(), locale.getCountry() ) :
                locale.getLanguage();
    }

    /**
     * 获取本机语言
     * @return  语言。eg：zh
     */
    public static String getLanguage() { return getLanguage( false ); }

    /**
     * 打开谷歌商店
     * @param context       上下文
     * @return              结果
     */
    public static boolean startGooglePlay(Context context) {
        if( context == null ) return false;
        try {
            String packageName = AppUtil.getPackageName( context );
            Intent intent = new Intent( Intent.ACTION_VIEW );

            intent.setData( Uri.parse( "market://details?id=" + packageName ) );
            //谷歌商店的包名
            intent.setPackage( "com.android.vending" );
            //打开谷歌商店，如果打开失败，则用浏览器打开
            if( startActivity( context, intent ) ) return true;
            /* 通过浏览器打开 */
            intent.setData(
                    Uri.parse( "https://play.google.com/store/apps/details?id=" + packageName )
            );
            intent.setPackage( null );
            return startActivity( context, intent );
        } catch (ActivityNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean startActivity(Context context, Intent intent) {
        try {
            context.startActivity( intent );
            return true;
        }catch(Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     复制到剪贴板
     @param context     上下文
     @param label       用户可见的标签
     @param content     内容
     @return            是否复制成功
     */
    public static boolean copyTextToClipboard(Context context, String label, String content) {
        if( context == null ) return false;
        ClipboardManager cm = (ClipboardManager) context.getSystemService( Activity.CLIPBOARD_SERVICE );
        if( cm == null ) return false;
        ClipData data = ClipData.newPlainText(label, content);
        try {
            cm.setPrimaryClip( data );
            return true;
        } catch(Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    public static boolean copyTextToClipboard(Context context, String content) {
        return copyTextToClipboard( context, "text", content );
    }

    /**
     从剪贴板中粘贴文本
     @param context     上下文
     @param isTopData   是否只获取第一条文本
     @return            剪贴数据
     */
    @Nullable
    public static ClipData.Item[] pasteTextFromClipboards(Context context, boolean isTopData) {
        if( context == null ) return null;
        ClipboardManager cm = (ClipboardManager) context.getSystemService( Activity.CLIPBOARD_SERVICE );
        if( cm == null ) return null;
        ClipData data = cm.getPrimaryClip();
        if( data == null || data.getItemCount() == 0 ) return null;
        //只返回剪贴板第一条数据
        if( isTopData ) {
            return new ClipData.Item[] { data.getItemAt( 0 ) };
        }

        ClipData.Item[] items = new ClipData.Item[ data.getItemCount() ];
        for( int i = 0; i < items.length; i++ ) items[ i ] = data.getItemAt( i );
        return items;
    }

    /**
     从剪贴板中粘贴文本 - 高版本兼容
     @param context     上下文
     @param isTopData   是否只获取第一条文本
     @param call        剪贴数据
     */
    public static void pasteTextFromClipboards(Context context, boolean isTopData,
                                               Consumer<ClipData.Item[]> call) {
        HandlerManage.create().post( () -> {
            if( call == null ) return;
            call.accept( context == null ? null : pasteTextFromClipboards( context, isTopData ) );
        }, 1000);
    }

    /**
     从剪贴板中粘贴文本 - 高版本兼容
     @param context     上下文
     @param call        剪贴数据
     */
    public static void pasteTextFromClipboards(Context context, Consumer<ClipData.Item[]> call) {
        HandlerManage.create().post( () -> {
            if( call == null ) return;
            call.accept( context == null ? null : pasteTextFromClipboards( context, false ) );
        }, 1000);
    }

    /**
     从剪贴板中粘贴文本
     @param context     上下文
     @return            剪贴数据
     */
    @Nullable
    public static ClipData.Item pasteTextFromClipboard(Context context) {
        if( context == null ) return null;
        ClipData.Item[] items = pasteTextFromClipboards( context, true );
        return items != null && items.length > 0 ? items[ 0 ] : null;
    }

    /**
     从剪贴板中粘贴文本 - 高版本兼容
     @param context     上下文
     @param call        剪贴数据
     */
    public static void pasteTextFromClipboard(Context context,
                                              Consumer<ClipData.Item> call) {
        HandlerManage.create().post( () -> {
            if( call == null ) return;
            call.accept( context == null ? null : pasteTextFromClipboard( context ) );
        }, 1000);
    }

    /**
     从剪贴板中粘贴文本
     @param context     上下文
     @return            剪贴数据
     */
    @Nullable
    public static String pasteTextFromClipboardOfString(Context context) {
        if( context == null ) return null;
        ClipData.Item item = pasteTextFromClipboard( context );
        return item != null ? item.getText().toString() : null;
    }

    /**
     从剪贴板中粘贴文本 - 高版本兼容
     @param context     上下文
     @param call        剪贴数据
     */
    public static void pasteTextFromClipboardOfString(Context context,
                                                      Consumer<String> call) {
        HandlerManage.create().post( () -> {
            if( call == null ) return;
            call.accept( context == null ? null : pasteTextFromClipboardOfString( context ) );
        }, 1000);
    }

    /**
     添加对系统屏幕显示高度的监听
     @param w           {@link Activity#getWindow()}
     @param l           监听器
     */
    public static void addOnWindowVisibleDisplayListener(Window w,
                                                         @NonNull OnWindowVisibleDisplayListener l) {
        if( w == null ) return;
        mOnWindowVisibleDisplayList.add( l );

        mOnGlobalLayoutListener = () -> {
            w.getDecorView().getWindowVisibleDisplayFrame( mWindowVisibleDisplayRect );
            int h = w.getDecorView().getHeight() - mWindowVisibleDisplayRect.bottom;
            if( mRootInvisibleHeight != h ) {
                l.onDisplay( mRootInvisibleHeight, h >= mRootInvisibleHeight );
            }
            mRootInvisibleHeight = h;
        };
        w.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener( mOnGlobalLayoutListener );
    }

    /**
     移除对系统屏幕显示高度的监听
     @param w           {@link Activity#getWindow()}
     @param l           监听器
     */
    public static void removeOnWindowVisibleDisplayListener(Window w,
                                                            @NonNull OnWindowVisibleDisplayListener l) {
        if( w == null ) return;
        mOnWindowVisibleDisplayList.remove( l );
        if( mOnWindowVisibleDisplayList.size() > 0 ) return;

        ViewTreeObserver vto = w.getDecorView().getViewTreeObserver();
        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ) {
            vto.removeOnGlobalLayoutListener( mOnGlobalLayoutListener );
        }else {
            vto.removeGlobalOnLayoutListener( mOnGlobalLayoutListener );
        }
    }

    /**
     安装Apk安装包
     @param context         上下文
     @param filePath        文件路径
     @return                安装结果
     */
    public static boolean installApk(Context context, String filePath) {
        if( context == null || TextUtils.isEmpty( filePath ) ) return false;
        try {
            Intent intent = new Intent( Intent.ACTION_VIEW );
            intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
            if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
                //增加读写权限
                intent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION );
            }
            intent.setDataAndType(
                    filePathToUri( context, filePath ), "application/vnd.android.package-archive"
            );
            context.startActivity( intent );
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     通过文件路径获取Uri
     @param context         上下文
     @param filePath        文件路径
     @return                文件Uri
     */
    private static Uri filePathToUri(Context context, String filePath) {
        if( context == null || TextUtils.isEmpty( filePath ) ) return null;
        File f = new File( filePath );
        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
            return FileProvider.getUriForFile(
                    context,
                    context.getPackageName() + ".fileProvider",
                    f
            );
        } else {
            return Uri.fromFile( f );
        }
    }

    /**
     获取Apk文件信息
     @param context         上下文
     @param apkPath         apk文件路径
     @return                包信息
     */
    @Nullable
    public static PackageInfo getApkOfPackageInfo(Context context, String apkPath) {
        if( context == null ) return null;
        PackageManager pm = context.getPackageManager();
        if( pm == null ) return null;
        PackageInfo info = pm.getPackageArchiveInfo( apkPath, PackageManager.GET_ACTIVITIES );
        if( info == null ) return null;
        ApplicationInfo appInfo = info.applicationInfo;
        if( appInfo == null ) return null;
        try {
            appInfo.sourceDir = apkPath;
            appInfo.publicSourceDir = apkPath;
            return info;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        }
        return null;
    }
}